﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace uk.ac.soton.ses
{
    /// <summary>
    /// Provides a wrapper class for parsing an xtekct file, or a file with key=value data
    /// </summary>
    public class FileParser_XTEKCT
    {
        private string _fileName = "";

        /// <summary>
        /// This type of file can be broken into sections by providing a section name surrounded by square brackets, e.g. [section_1]
        /// </summary>
        public List<XTEKCTFileSection> Sections { get; set; }

        /// <summary>
        /// A list of key value pairs representing the properties set in the file at the top level, where the key is the property name and value is the property value
        /// Properties set within sections are stored in the Sections property
        /// </summary>
        public List<KeyValuePair<string, string>> Properties { get; set; }

        /// <summary>
        /// Constructor to initialise the class with a specified file. The file is read and properties populated by the constructor
        /// </summary>
        /// <param name="fileName">The path to the file to parse</param>
        public FileParser_XTEKCT(string fileName)
        {
            this._fileName = fileName;
            this.Sections = new List<XTEKCTFileSection>();
            this.Properties = new List<KeyValuePair<string, string>>();
            this.ReadXtekFile();
        }

        /// <summary>
        /// As this is a proof of concept, we only support files 5 megabytes or below. This function checks to see if the file is a sensible file size.
        /// </summary>
        /// <returns>true if file is a supported size, otherwise false</returns>
        private bool FileSizeSupported()
        {
            //Check size
            FileInfo fi = new FileInfo(this._fileName);
            return (fi.Length <= (5120 * 1024));
        }

        /// <summary>
        /// Makes a quick check for appropriately formatted data in the beginning of the file
        /// </summary>
        /// <returns>true if the file seems to be correctly formatted, otherwise false</returns>
        private bool FileIsValid()
        {
            //Quick check for an '=' inside first 500 chars
            //Not the best error check as there will be edge cases, but simple
            using (StreamReader sr = new StreamReader(this._fileName))
            {
                bool equalsFound = false;
                char[] charTest = new char[500];
                int read = sr.Read(charTest, 0, charTest.Length);
                string charTestStr = new string(charTest);
                foreach (char ch in charTest)
                {
                    switch (ch)
                    {
                        case '=':
                            equalsFound = true;
                            break;
                    }
                }
                return equalsFound;
            }
        }

        /// <summary>
        /// Read the contents of the file and parse, setting the Properties and Sections properties.
        /// </summary>
        private void ReadXtekFile()
        {
            //Check file is valid
            if (!this.FileSizeSupported())
            {
                throw new InvalidDataException("File is too big");
            }

            if(!this.FileIsValid())
            {
                throw new InvalidDataException("File does not seem to be in the correct format");
            }

            using (StreamReader sr = new StreamReader(this._fileName))
            {
                string readLine;
                List<KeyValuePair<string, string>> targetPropertiesList = this.Properties;
                
                while ((readLine = sr.ReadLine()) != null)
                {
                    string line = readLine.Trim();
                    string sectionName = String.Empty;
                    string key = String.Empty;
                    string value = String.Empty;

                    //Check for a blank line
                    if (string.IsNullOrEmpty(line))
                    {
                        continue;
                    }

                    //Check if it's a section
                    if (line.StartsWith("["))
                    {
                        //Check it is defined properly
                        if (line.EndsWith("]"))
                        {
                            sectionName = line.Substring(1, line.Length - 2);
                            //Create a new section
                            XTEKCTFileSection section = new XTEKCTFileSection(sectionName);
                            this.Sections.Add(section);
                            //Make sure we add new properties to the newly created section
                            targetPropertiesList = section.Properties;
                        }
                        //We don't know what happened with this line so we better give up
                        else
                        {
                            throw new InvalidDataException("Section name should end with ']'");
                        }
                    }
                    //Anything else we hope is a property
                    else
                    {
                        string[] keyAndValue = line.Split("=".ToCharArray(), 2);
                        if (keyAndValue.Length == 2)
                        {
                            key = keyAndValue[0];
                            value = keyAndValue[1];
                            targetPropertiesList.Add(new KeyValuePair<string, string>(key, value));
                        }
                        else
                        {
                            //Problem reading this line because it's not a section, or a key-value pair
                            throw new InvalidDataException("Key-value pair should contain '='");
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// A class used to record the properties of the sections in an XTEKCT file
    /// </summary>
    public class XTEKCTFileSection
    {
        /// <summary>
        /// The name of the section, defined in square brackets before the accompanying properties, e.g. [section_1]
        /// </summary>
        public string SectionName { get; set; }
        
        /// <summary>
        /// A list of key-value pairs representing the properties set within the section, where the key is the property name and value is the property value
        /// </summary>
        public List<KeyValuePair<string, string>> Properties { get; set; }

        /// <summary>
        /// Constructor to setup the class with the section name
        /// </summary>
        /// <param name="sectionName">The name of the section</param>
        public XTEKCTFileSection(string sectionName)
        {
            this.Properties = new List<KeyValuePair<string, string>>();
            this.SectionName = sectionName;
        }
    }
}
